/*
 * Copyright (C) 2008 Apple Computer, Inc.  All rights reserved.
 * Copyright (C) 2012 Sony Computer Entertainment Inc.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "WebViewManager.h"

#include "MGWebViewInternal.h"
#include "RunLoop.h"
#include "WebViewHitTestResult.h"
#include <manx/KeyboardCodes.h>
#include <manx/RunLoop.h>
#include <pthread.h>
#include <stdio.h>
#include <wtf/MainThread.h>

#if OS(PSP2)
#include <sched.h>
#endif

namespace MXGlue {

static inline Manx::KeyboardEvent::Type manxKeyEvent(KeyEventType event)
{
    switch (event) {
    case KEY_EVENT_PRESSED:     return Manx::KeyboardEvent::KeyDown;
    case KEY_EVENT_RELEASED:    return Manx::KeyboardEvent::KeyUp;
    default:                    return Manx::KeyboardEvent::KeyDown;
    }
}

static inline int manxKey(KeyType key)
{
    switch (key) {
    case KEY_A:                 return Manx::VK_A;
    case KEY_B:                 return Manx::VK_B;
    case KEY_C:                 return Manx::VK_C;
    case KEY_D:                 return Manx::VK_D;
    case KEY_E:                 return Manx::VK_E;
    case KEY_F:                 return Manx::VK_F;
    case KEY_G:                 return Manx::VK_G;
    case KEY_H:                 return Manx::VK_H;
    case KEY_I:                 return Manx::VK_I;
    case KEY_J:                 return Manx::VK_J;
    case KEY_K:                 return Manx::VK_K;
    case KEY_L:                 return Manx::VK_L;
    case KEY_M:                 return Manx::VK_M;
    case KEY_N:                 return Manx::VK_N;
    case KEY_O:                 return Manx::VK_O;
    case KEY_P:                 return Manx::VK_P;
    case KEY_Q:                 return Manx::VK_Q;
    case KEY_R:                 return Manx::VK_R;
    case KEY_S:                 return Manx::VK_S;
    case KEY_T:                 return Manx::VK_T;
    case KEY_U:                 return Manx::VK_U;
    case KEY_V:                 return Manx::VK_V;
    case KEY_W:                 return Manx::VK_W;
    case KEY_X:                 return Manx::VK_X;
    case KEY_Y:                 return Manx::VK_Y;
    case KEY_Z:                 return Manx::VK_Z;
    case KEY_1:                 return Manx::VK_1;
    case KEY_2:                 return Manx::VK_2;
    case KEY_3:                 return Manx::VK_3;
    case KEY_4:                 return Manx::VK_4;
    case KEY_5:                 return Manx::VK_5;
    case KEY_6:                 return Manx::VK_6;
    case KEY_7:                 return Manx::VK_7;
    case KEY_8:                 return Manx::VK_8;
    case KEY_9:                 return Manx::VK_9;
    case KEY_0:                 return Manx::VK_0;

    case KEY_F1:                return Manx::VK_F1;
    case KEY_F2:                return Manx::VK_F2;
    case KEY_F3:                return Manx::VK_F3;
    case KEY_F4:                return Manx::VK_F4;
    case KEY_F5:                return Manx::VK_F5;
    case KEY_F6:                return Manx::VK_F6;
    case KEY_F7:                return Manx::VK_F7;
    case KEY_F8:                return Manx::VK_F8;
    case KEY_F9:                return Manx::VK_F9;
    case KEY_F10:               return Manx::VK_F10;
    case KEY_F11:               return Manx::VK_F11;
    case KEY_F12:               return Manx::VK_F12;

    case KEY_ACCENT:            return Manx::VK_OEM_3;
    case KEY_BACKSLASH:         return Manx::VK_OEM_2;
    case KEY_BACKSPACE:         return Manx::VK_BACK;
    case KEY_CAPS_LOCK:         return Manx::VK_CAPITAL;
    case KEY_COMMA:             return Manx::VK_OEM_COMMA;
    case KEY_DELETE:            return Manx::VK_DELETE;
    case KEY_DOWN_ARROW:        return Manx::VK_DOWN;
    case KEY_END:               return Manx::VK_END;
    case KEY_ENTER:             return Manx::VK_RETURN;
    case KEY_EQUAL:             return Manx::VK_OEM_PLUS;
    case KEY_ESC:               return Manx::VK_ESCAPE;
    case KEY_HENKAN:            return Manx::VK_CONVERT;
    case KEY_HOME:              return Manx::VK_HOME;
    case KEY_INSERT:            return Manx::VK_INSERT;
    case KEY_KANA:              return Manx::VK_KANA;
    case KEY_KANJI:             return Manx::VK_KANJI;
    case KEY_LEFT_ARROW:        return Manx::VK_LEFT;
    case KEY_LEFT_BRACKET:      return Manx::VK_OEM_4;
    case KEY_MINUS:             return Manx::VK_OEM_MINUS;
    case KEY_MUHENKAN:          return Manx::VK_NONCONVERT;
    case KEY_PAGE_DOWN:         return Manx::VK_NEXT;
    case KEY_PAGE_UP:           return Manx::VK_PRIOR;
    case KEY_PAUSE:             return Manx::VK_PAUSE;
    case KEY_PERIOD:            return Manx::VK_OEM_PERIOD;
    case KEY_PRINTSCREEN:       return Manx::VK_PRINT;
    case KEY_QUOTATION:         return Manx::VK_OEM_7;
    case KEY_RIGHT_ARROW:       return Manx::VK_RIGHT;
    case KEY_RIGHT_BRACKET:     return Manx::VK_OEM_6;
    case KEY_SCROLL_LOCK:       return Manx::VK_SCROLL;
    case KEY_SEMICOLON:         return Manx::VK_OEM_1;
    case KEY_SLASH:             return Manx::VK_OEM_5;
    case KEY_SPACE:             return Manx::VK_SPACE;
    case KEY_TAB:               return Manx::VK_TAB;
    case KEY_UP_ARROW:          return Manx::VK_UP;

    case KEY_NUMPAD_1:          return Manx::VK_NUMPAD1;
    case KEY_NUMPAD_2:          return Manx::VK_NUMPAD2;
    case KEY_NUMPAD_3:          return Manx::VK_NUMPAD3;
    case KEY_NUMPAD_4:          return Manx::VK_NUMPAD4;
    case KEY_NUMPAD_5:          return Manx::VK_NUMPAD5;
    case KEY_NUMPAD_6:          return Manx::VK_NUMPAD6;
    case KEY_NUMPAD_7:          return Manx::VK_NUMPAD7;
    case KEY_NUMPAD_8:          return Manx::VK_NUMPAD8;
    case KEY_NUMPAD_9:          return Manx::VK_NUMPAD9;
    case KEY_NUMPAD_0:          return Manx::VK_NUMPAD0;
    case KEY_NUMPAD_ASTERISK:   return Manx::VK_MULTIPLY;
    case KEY_NUMPAD_ENTER:      return Manx::VK_RETURN;
    case KEY_NUMPAD_MINUS:      return Manx::VK_SUBTRACT;
    case KEY_NUMPAD_NUMLOCK:    return Manx::VK_NUMLOCK;
    case KEY_NUMPAD_PERIOD:     return Manx::VK_DECIMAL;
    case KEY_NUMPAD_PLUS:       return Manx::VK_ADD;
    case KEY_NUMPAD_SLASH:      return Manx::VK_DIVIDE;

    case KEY_CARET_MOVE_LEFT:   return Manx::VKX_PS_CARET_LEFT;
    case KEY_CARET_MOVE_RIGHT:  return Manx::VKX_PS_CARET_RIGHT;
    case KEY_CARET_MOVE_UP:     return Manx::VKX_PS_CARET_UP;
    case KEY_CARET_MOVE_DOWN:   return Manx::VKX_PS_CARET_DOWN;

    default:                    return 0;
    }
}

static inline bool isKeyPad(KeyType key)
{
    switch (key) {
    case KEY_NUMPAD_1:
    case KEY_NUMPAD_2:
    case KEY_NUMPAD_3:
    case KEY_NUMPAD_4:
    case KEY_NUMPAD_5:
    case KEY_NUMPAD_6:
    case KEY_NUMPAD_7:
    case KEY_NUMPAD_8:
    case KEY_NUMPAD_9:
    case KEY_NUMPAD_0:
    case KEY_NUMPAD_ASTERISK:
    case KEY_NUMPAD_ENTER:
    case KEY_NUMPAD_MINUS:
    case KEY_NUMPAD_NUMLOCK:
    case KEY_NUMPAD_PERIOD:
    case KEY_NUMPAD_PLUS:
    case KEY_NUMPAD_SLASH:
        return true;
    default:
        return false;
    }
}


/* class WebViewManager */
#if OS(PSP2)
#define WEBVIEW_THREAD_PRIORITY     21 // offset from user default
#else
#define WEBVIEW_THREAD_PRIORITY     1001 // 1001 (highest) - 3071
#endif
#define WEBVIEW_THREAD_STACK_SIZE   (1024 * 1024) // 1MB
#define WEBVIEW_THREAD_NAME         "SceWebViewManagerRunLoop" // thread name

bool WebViewManager::s_initialized = false;

static pthread_t gWebThread;

WebViewQueue* WebViewManager::s_instanceQueue;
WebViewQueue* WebViewManager::s_commandQueue;
WebViewQueue* WebViewManager::s_resultQueue;

WebViewManager::CallbackHandler WebViewManager::s_callbackHandler = 0;
void* WebViewManager::s_callbackUserData = 0;
CallbackData WebViewManager::s_callbackIn[16];
CallbackData WebViewManager::s_callbackOut[16];

bool WebViewManager::init(bool, CallbackHandler handler, void* userData)
{
    ASSERT(!s_initialized);
    if (s_initialized)
        return false;

    Manx::RunLoop::init();

    s_instanceQueue = new WebViewQueue();
    ASSERT(s_instanceQueue);

    // prepare async thread or sync init
    {
        // set callback data
        s_callbackHandler = handler;
        s_callbackUserData = userData;

        // prepare async command queue and result queue
        s_commandQueue = new WebViewQueue();
        ASSERT(s_commandQueue);
        s_resultQueue = new WebViewQueue();
        ASSERT(s_resultQueue);

        // create the update thread
        pthread_attr_t attr;
        pthread_attr_init(&attr);
#if OS(PSP2)
        attr.priority += WEBVIEW_THREAD_PRIORITY;
        attr.stacksize = WEBVIEW_THREAD_STACK_SIZE;
        attr.name = (char*)WEBVIEW_THREAD_NAME;
#else
        pthread_attr_setstacksize(&attr, WEBVIEW_THREAD_STACK_SIZE);
#endif
        int ret = pthread_create(&gWebThread, &attr, &processThread, 0);
        if (ret) {
            printf("WebView::Create: pthread_create() failed!\n");
            return false;
        }

        // Wait for finish initWebCore.
        WebViewCommand* res = popResult();
        delete res;
    }

    s_initialized = true;

    return true;
}

bool WebViewManager::exit()
{
    ASSERT(s_initialized);
    if (!s_initialized)
        return false;

    s_initialized = false;

    // async cleanup or sync exit
    {
        // push exit command to command queue for thread to exit
        WebViewCommand* cmd = new WebViewCommand(0, WEBVIEW_COMMAND_EXIT);
        WebViewManager::pushCommand(cmd);

        // wait for thread completion
        pthread_join(gWebThread, 0);

        // cleanup async command queue
        ASSERT(!s_commandQueue->count());
        delete s_commandQueue;

        // cleanup result command queue
        ASSERT(!s_resultQueue->count());
        delete s_resultQueue;
    }
    // destroy the instance queue
    delete s_instanceQueue;

    return true;
}

bool WebViewManager::update()
{
    ASSERT(s_initialized);
    if (!s_initialized)
        return false;

    return true;
}

WebView* WebViewManager::create(int width, int height, void* pool, void* painter, bool enableGeolocation)
{
    ASSERT(s_initialized);
    if (!s_initialized)
        return 0;

    if (!width || !height)
        return 0;

    WebView* webview;

    if (!isWebThread()) {
        WebViewCommand* cmd = new WebViewCommand(0, WEBVIEW_COMMAND_CREATE);
        cmd->m_data[0].typeInt = width;
        cmd->m_data[1].typeInt = height;
        cmd->m_data[2].typeVoidPtr = pool;
        cmd->m_data[3].typeVoidPtr = painter;
        cmd->m_data[4].typeBool = enableGeolocation;
        pushCommand(cmd);
        WebViewCommand* res= popResult();
        webview = res->m_webView;
        delete res;
    } else {
        WebKit::WebViewCreateParam param = { width, height, pool, painter, enableGeolocation};
        webview = new WebView(param);
        ASSERT(webview);

        WebViewQueueItem* item = new WebViewQueueItem((uintptr_t)webview);
        ASSERT(item);

        // add the webview pointer to the instance queue
        s_instanceQueue->push(item);
    }

    return webview;
}

bool WebViewManager::createWebViewAsync(int width, int height, void* userArg, void* pool, void* painter, bool enableGeolocation)
{
    ASSERT(s_initialized);
    if (!s_initialized)
        return false;

    if (!width || !height)
        return false;

    if (!isWebThread()) {
        WebViewCommand* cmd = new WebViewCommand(0, WEBVIEW_COMMAND_CREATE_ASYNC);
        cmd->m_data[0].typeInt = width;
        cmd->m_data[1].typeInt = height;
        cmd->m_data[2].typeVoidPtr = pool;
        cmd->m_data[3].typeVoidPtr = painter;
        cmd->m_data[4].typeBool = enableGeolocation;
        cmd->m_data[5].typeVoidPtr = userArg;
        pushCommand(cmd);
    } else 
        ASSERT(0);
    
    return true;
}

bool WebViewManager::destroy(WebView* webview)
{
    ASSERT(s_initialized);
    if (!s_initialized)
        return false;

    if (!isWebThread()) {
        WebViewCommand* cmd = new WebViewCommand(webview, WEBVIEW_COMMAND_DESTROY);
        pushCommand(cmd);
        // WebViewCommand* res= popResult();
        // delete res;
    } else {
        // remove the webview pointer from the instance queue
        for (WebViewQueueItem* ptr = s_instanceQueue->head(); ptr; ptr = ptr->next()) {
            if ((WebView*)ptr->getId() == webview) {
                s_instanceQueue->remove(ptr);
                delete webview;
                break;
            }
        }

    }

    return true;
}

void* WebViewManager::processThread(void*)
{
    WebKit::WebViewManager::initWebCore();

    WebViewCommand* res = new WebViewCommand(0, WEBVIEW_COMMAND_INITIALIZED);
    s_resultQueue->push(res);

    WebCore::RunLoop::run();

    WebKit::WebViewManager::exitWebCore();

    return 0;
}

void WebViewManager::processCommand()
{
    bool exit = false;
    s_commandQueue->lock();
    int dispatchcount = s_commandQueue->count();
    s_commandQueue->unlock();
    WebViewCommand* cmd = 0;
    while (dispatchcount && !exit) {
        cmd = (WebViewCommand*)s_commandQueue->pop();
        if (!cmd)
            break;
        dispatchcount--;
        WebView* webview = cmd->m_webView;
        WebKit::WebView* impl = webview ? webview->m_impl : 0;

        switch (cmd->getId()) {
        case WEBVIEW_COMMAND_EXIT:
            // destroy any webview instances left in instance queue
            WebViewQueueItem* ptr;
            while ((ptr = s_instanceQueue->pop())) {
                delete (WebView*)ptr->getId();
                delete ptr;
            }
            exit = true;
            break;

        case WEBVIEW_COMMAND_CREATE:
            {
                WebKit::WebViewCreateParam param = { cmd->m_data[0].typeInt, cmd->m_data[1].typeInt, cmd->m_data[2].typeVoidPtr, cmd->m_data[3].typeVoidPtr, cmd->m_data[4].typeBool};
                webview = new WebView(param);
                ASSERT(webview);

                WebViewQueueItem* item = new WebViewQueueItem((uintptr_t)webview);
                ASSERT(item);

                // add the webview pointer to the instance queue
                s_instanceQueue->push(item);

                WebViewCommand* res = new WebViewCommand(webview, (WebViewCommandType)cmd->getId());
                s_resultQueue->push(res);
            }
            break;

        case WEBVIEW_COMMAND_CREATE_ASYNC:
            {
                WebKit::WebViewCreateParam param = { cmd->m_data[0].typeInt, cmd->m_data[1].typeInt, cmd->m_data[2].typeVoidPtr, cmd->m_data[3].typeVoidPtr, cmd->m_data[4].typeBool};
                webview = new WebView(param);
                ASSERT(webview);

                WebViewManager::s_callbackIn[0].type_void_ptr = cmd->m_data[5].typeVoidPtr;
                if (!WebViewManager::processCallback(webview, CALLBACK_WINDOW_CREATED))
                    delete webview;
                else {
                    WebViewQueueItem* item = new WebViewQueueItem((uintptr_t)webview);
                    ASSERT(item);

                    // add the webview pointer to the instance queue
                    s_instanceQueue->push(item);
                }
            }
            break;

        case WEBVIEW_COMMAND_DESTROY:
            // remove the webview pointer from the instance queue
            for (WebViewQueueItem* ptr = s_instanceQueue->head(); ptr; ptr = ptr->next()) {
                if ((WebView*)ptr->getId() == webview) {
                    s_instanceQueue->remove(ptr);
                    delete webview;
                    break;
                }
            }

            // res = new WebViewCommand(0, (WebViewCommandType)cmd->getId());
            // s_resultQueue->push(res);

            break;

        case WEBVIEW_COMMAND_LOAD_URL:
            impl->loadURL(cmd->m_data[0].typeCharPtr);
            delete [] cmd->m_data[0].typeCharPtr;
            break;

        case WEBVIEW_COMMAND_LOAD_HTML:
            impl->loadHTML(cmd->m_data[0].typeCharPtr, cmd->m_data[1].typeCharPtr);
            delete [] cmd->m_data[0].typeCharPtr;
            delete [] cmd->m_data[1].typeCharPtr;
            break;

        case WEBVIEW_COMMAND_LOAD_DATA:
            impl->loadData(cmd->m_data[0].typeCharPtr, cmd->m_data[1].typeLong, cmd->m_data[2].typeCharPtr, cmd->m_data[3].typeCharPtr, cmd->m_data[4].typeCharPtr);
            delete [] cmd->m_data[0].typeCharPtr;
            delete [] cmd->m_data[2].typeCharPtr;
            delete [] cmd->m_data[3].typeCharPtr;
            delete [] cmd->m_data[4].typeCharPtr;
            break;

        case WEBVIEW_COMMAND_LOAD_ARCHIVE:
            impl->loadArchive(cmd->m_data[0].typeCharPtr, cmd->m_data[1].typeLong, cmd->m_data[2].typeCharPtr);
            delete [] cmd->m_data[0].typeCharPtr;
            delete [] cmd->m_data[2].typeCharPtr;
            break;

        case WEBVIEW_COMMAND_RELOAD:
            impl->reload();
            break;

        case WEBVIEW_COMMAND_STOP:
            impl->stop();
            break;

        case WEBVIEW_COMMAND_GO_BACK:
            impl->goBack();
            break;

        case WEBVIEW_COMMAND_GO_FORWARD:
            impl->goForward();
            break;

        case WEBVIEW_COMMAND_INCREASE_TEXT_SIZE:
            impl->increaseTextSize();
            break;

        case WEBVIEW_COMMAND_DECREASE_TEXT_SIZE:
            impl->decreaseTextSize();
            break;

        case WEBVIEW_COMMAND_RESET_TEXT_SIZE:
            impl->resetTextSize();
            break;

        case WEBVIEW_COMMAND_SCROLL_EVENT:
            // FIXME: Not implemented.
            break;

        case WEBVIEW_COMMAND_DRAW_RECT:
            impl->drawRect(cmd->m_data[0].typeInt,
                cmd->m_data[1].typeInt,
                cmd->m_data[2].typeInt,
                cmd->m_data[3].typeInt,
                0,
                0);
            break;

        case WEBVIEW_COMMAND_SET_CLIP_RECT:
            impl->applyClipRect();
            break;

        case WEBVIEW_COMMAND_MOUSE_EVENT:
        case WEBVIEW_COMMAND_MOUSE_EVENT_MOVE:
            impl->sendMouseEvent(cmd->m_data[0].typeMouseEvent,
                cmd->m_data[1].typeMouseButton,
                cmd->m_data[2].typeInt,
                cmd->m_data[3].typeInt);
            if (MOUSE_EVENT_RELEASED == cmd->m_data[0].typeMouseEvent)
                webview->checkInputMethodStateWT();
            break;

        case WEBVIEW_COMMAND_KEY_EVENT:
            {
                KeyEventType event = cmd->m_data[0].typeKeyEvent;
                KeyType key = cmd->m_data[1].typeKey;
                // char ascii = cmd->m_data[2].typeChar;
                bool shift = cmd->m_data[3].typeBool;
                bool ctrl = cmd->m_data[4].typeBool;
                bool alt = cmd->m_data[5].typeBool;
                bool win = cmd->m_data[6].typeBool;
                KeyDefaultBehavior behavior = static_cast<KeyDefaultBehavior>(cmd->m_data[7].typeInt);
                Manx::KeyboardEvent manxEvent(manxKeyEvent(event), manxKey(key), isKeyPad(key), shift, ctrl, alt, win);
                bool consumed = impl->sendKeyEvent(manxEvent, behavior);
                WebViewManager::s_callbackIn[0].type_bool = consumed;
                WebViewManager::s_callbackIn[1].type_int = event;
                WebViewManager::s_callbackIn[2].type_int = key;
                WebViewManager::processCallback(webview, CALLBACK_DID_RECEIVE_KEY_EVENT2);
            }
            break;

        case WEBVIEW_COMMAND_HTML_EVENT:
            impl->confirmComposition(cmd->m_data[1].typeHtmlEventDataPtr[0].type_const_char_ptr, 1);
            if (cmd->m_data[1].typeHtmlEventDataPtr)
                delete [] cmd->m_data[1].typeHtmlEventDataPtr;
            break;
        case WEBVIEW_COMMAND_TOUCH_EVENT:
            {
            impl->applyClipRect();
            bool prevented = impl->sendTouchEvent(cmd->m_data[0].typeTouchEventDataPtr);
            if (cmd->m_data[0].typeTouchEventDataPtr)
                delete [] cmd->m_data[0].typeTouchEventDataPtr;
            WebViewManager::s_callbackIn[0].type_bool = prevented;
            WebViewManager::processCallback(webview, CALLBACK_IS_DEFAULT_PREVENTED);
            break;
            }
        case WEBVIEW_COMMAND_SET_CARET_VISIBLE:
            impl->setCaretVisible(cmd->m_data[0].typeBool);
            break;
        case WEBVIEW_COMMAND_SET_COMPOSITION:
            impl->setComposition(cmd->m_data[0].typeCharPtr, cmd->m_data[1].typeInt, cmd->m_data[2].typeInt);
            if (cmd->m_data[0].typeCharPtr)
                delete [] cmd->m_data[0].typeCharPtr;
            break;
        case WEBVIEW_COMMAND_CONFIRM_COMPOSITION:
            impl->confirmComposition(cmd->m_data[0].typeCharPtr, cmd->m_data[1].typeInt);
            if (cmd->m_data[0].typeCharPtr)
                delete [] cmd->m_data[0].typeCharPtr;
            break;
        case WEBVIEW_COMMAND_CANCEL_COMPOSITION:
            impl->cancelComposition();
            break;
        case WEBVIEW_COMMAND_EXIT_COMPOSITION:
            impl->exitComposition();
            break;
        case WEBVIEW_COMMAND_CLEAR_TEXT:
            impl->clearText();
            break;
        case WEBVIEW_COMMAND_HITTEST:
            {
                Manx::Point point(cmd->m_data[0].typeInt, cmd->m_data[1].typeInt);
                WebViewHitTestResult result;
                bool elementForm = false;
                impl->hitTest(point, result, elementForm);
                WebViewManager::s_callbackIn[0].type_void_ptr = (void*)&result;
                WebViewManager::s_callbackIn[1].type_int= cmd->m_data[2].typeInt;
                WebViewManager::s_callbackIn[2].type_bool= elementForm;
                WebViewManager::processCallback(webview, CALLBACK_HITTEST);
            }
            break;
        case WEBVIEW_COMMAND_UPLOAD:
            impl->setUploadFile(cmd->m_data[0].typeCharPtr, cmd->m_data[1].typeCharPtr);
            delete [] cmd->m_data[0].typeCharPtr;
            delete [] cmd->m_data[1].typeCharPtr;
            break;
        case WEBVIEW_COMMAND_GESTURE_EVENT:
            impl->applyClipRect();
            impl->sendGestureEvent(cmd->m_data[0].typeChar, cmd->m_data[0].typeInt, cmd->m_data[1].typeInt);
            webview->checkInputMethodStateWT();
            break;
        case WEBVIEW_COMMAND_SET_SETTINGS_JS:
            impl->settings().setScriptEnabled(cmd->m_data[0].typeBool);
            break;
        case WEBVIEW_COMMAND_SET_SETTINGS_COOKIE:
            impl->settings().setCookieEnabled(cmd->m_data[0].typeBool);
            break;
        case WEBVIEW_COMMAND_SET_SETTINGS_WEBGL:
            impl->settings().setWebGLEnabled(cmd->m_data[0].typeBool);
            break;
        case WEBVIEW_COMMAND_SELECT_POPUPMENU:
            impl->setSelectedItemsPopupMenu(cmd->m_data[0].typeInt, cmd->m_data[1].typeBool);
            break;
        case WEBVIEW_COMMAND_GETITEM_POPUPMENU:
            impl->getItemPopupMenu(cmd->m_data[0].typeInt);
            break;
        case WEBVIEW_COMMAND_SET_JSOBJ:
            break;
        case WEBVIEW_COMMAND_SET_USERAGENT:
            impl->setUserAgent(cmd->m_data[0].typeConstCharPtr);
            if (cmd->m_data[0].typeCharPtr)
                delete [] cmd->m_data[0].typeCharPtr;
            break;
        case WEBVIEW_COMMAND_SET_COOKIES:
            WebKit::WebViewManager::setCookies(cmd->m_data[0].typeCharPtr, cmd->m_data[1].typeCharPtr);
            if (cmd->m_data[0].typeCharPtr)
                delete [] cmd->m_data[0].typeCharPtr;
            if (cmd->m_data[1].typeCharPtr)
                delete [] cmd->m_data[1].typeCharPtr;
            break;
        case WEBVIEW_COMMAND_SET_SETTINGS_WEBSECURITY:
            impl->settings().setWebSecurityEnabled(cmd->m_data[0].typeBool);
            break;
        case WEBVIEW_COMMAND_SET_SETTINGS_DEVELOPEREXTRAS:
            impl->settings().setDeveloperExtrasEnabled(cmd->m_data[0].typeBool);
            break;
        case WEBVIEW_COMMAND_DELETE_ALL_COOKIES:
            WebKit::WebViewManager::deleteAllCookies();
            break;
        case WEBVIEW_COMMAND_RELEASE_TILES:
            impl->releaseTiles();
            break;
        case WEBVIEW_COMMAND_SET_LAYOUT_WIDTH:
            impl->setDefaultLayoutWidth(cmd->m_data[0].typeInt);
            break;
        case WEBVIEW_COMMAND_REQUEST_DL_IMAGE:
            webview->requestDownloadImageWT();
            break;
        case WEBVIEW_COMMAND_REQUEST_DL_IMAGE_DATA:
            webview->requestDownloadImageDataWT(cmd->m_data[0].typeInt, cmd->m_data[1].typeInt);
            break;
        case WEBVIEW_COMMAND_SET_GEOLOCATION_PERMISSION:
            impl->setGeolocationPermission(cmd->m_data[0].typeCharPtr, cmd->m_data[1].typeBool);
            if (cmd->m_data[0].typeCharPtr)
                delete [] cmd->m_data[0].typeCharPtr;
            break;
        case WEBVIEW_COMMAND_UPDATE_GEOLOCATION:
            impl->updateGeolocationPosition(static_cast<WebKit::WebViewGeolocationPosition*>(cmd->m_data[0].typeVoidPtr));
            break;
        case WEBVIEW_COMMAND_SET_JAVA_SCRIPT_OBJ:
            impl->setJavaScriptObj(static_cast<WebViewJavaScriptObject*>(cmd->m_data[0].typeVoidPtr));
            break;
        default:
            break;
        }

        delete cmd;
        if (exit)
            WebCore::RunLoop::current()->stop();
    }

    s_commandQueue->lock();
    bool needToSchedule = (s_commandQueue->count() > 0);
    s_commandQueue->unlock();
    if (needToSchedule)
        callOnMainThread(bind(processCommand));
}

bool WebViewManager::processCallback(WebView* webview, CallbackType type)
{
    if (!s_callbackHandler)
        return false;
    return s_callbackHandler(webview, s_callbackUserData, type, s_callbackIn, s_callbackOut);
}

void WebViewManager::pushCommand(WebViewCommand* cmd, int limit)
{
    s_commandQueue->lock();
    bool needToSchedule = (s_commandQueue->count() < 1);
    s_commandQueue->unlock();
    s_commandQueue->push(cmd, limit);
    if (needToSchedule)
        callOnMainThread(bind(processCommand));
}

WebViewCommand* WebViewManager::popResult()
{
    WebViewCommand* res;
    while (!(res = (WebViewCommand*)s_resultQueue->pop()))
        sched_yield();
    return res;
}

bool WebViewManager::isWebThread()
{
    return pthread_equal(gWebThread, pthread_self());
}

} // namespace MXGlue

